package panda.index.bplustree;

import panda.file.Block;
import panda.index.Index;
import panda.query.struct.*;
import panda.record.Schema;
import panda.record.TableToken;
import panda.record.TupleToken;
import panda.transaction.Transaction;
import static java.sql.Types.*;

public class TreeIndex implements Index {
	private Transaction tx;
	private TableToken dirTkn, leafTkn;
	private String dirfile, leaffile;
	private Block rootBlock;
	private TreeLeaf leaf = null;

	public static int status = -1;
	public static Constant currentSearchkey = null;

	public TreeIndex(String idx, Transaction t, Schema leafsch) {
		tx = t;
		leaffile = "leaf" + idx + ".bt";
		leafTkn = new TableToken(leaffile, leafsch);

		if (tx.getSize(leaffile) == 0) {
			tx.append(leafTkn.getFilename(), new FileHeader(leafTkn, -1));
		}

		Schema sch = new Schema();
		sch.addAttribute(leafsch.getAttributeByName("blocknum"), leafsch);
		sch.addAttribute(leafsch.getAttributeByName("dataval"), leafsch);
		dirfile = "dir" + idx + ".bt";
		dirTkn = new TableToken(dirfile, sch);
		rootBlock = new Block(dirTkn.getFilename(), 0);

		if (tx.getSize(dirTkn.getFilename()) == 0) {
			tx.append(dirTkn.getFilename(), new FileHeader(dirTkn, 0));
			TreePage page = new TreePage(rootBlock, dirTkn, tx);
			int fldtype = sch.getType("dataval");
			Constant tempmin = null;
			if (fldtype == INTEGER) {
				tempmin = new IntConstant(Integer.MIN_VALUE);
			} else {
				tempmin = new StringConstant("");
			}

			page.insertDir(0, tempmin, 0);
			page.close();
		}
	}

	public void findBeforeFirst(Constant searchkey) {
		close();
		TreeDir r = new TreeDir(rootBlock, dirTkn, tx);
		int blockno = r.search(searchkey);
		r.close();
		Block leafblk = new Block(leafTkn.getFilename(), blockno);
		leaf = new TreeLeaf(leafblk, leafTkn, searchkey, tx);
	}

	public void findSpecial(Constant searchkey, int op) {
		status = op;
		currentSearchkey = searchkey;

		if (op == LT || op == LTE) {
			findSmallestRecord();
		} else if (op == EQ || op == GTE) {
			if (!findTupleToken(searchkey)) {
				System.out.println("can not find the tuple");
			} else {
				System.out.println("you find it");
			}
		} else if (op == GT) {
			findGreaterThan(searchkey);
		}
	}
	
	private boolean findTupleToken(Constant searchkey) {
		findBeforeFirst(searchkey);
		return leaf.searchTupleToken(searchkey);
	} 

	// TODO`
	private void findGreaterThan(Constant searchkey) {
		findBeforeFirst(searchkey);
		leaf.SmallestBiggerTupleToken();
	}

	public void findSmallestRecord() {
		close();
		TreeDir r = new TreeDir(rootBlock, dirTkn, tx);
		int blockno = r.searchSmallestRecord();
		r.close();
		Block leafblk = new Block(leafTkn.getFilename(), blockno);
		TreePage temp = new TreePage(leafblk, leafTkn, tx);
		Constant key = temp.getData(0);
		//System.out.println("kyekeeyeekekekek " + key.getContentValue());
		leaf = new TreeLeaf(leafblk, leafTkn, key, tx);
	}

	public boolean hasNext() {
		if (leaf.isAtTail())
			return leaf.next();

		if (!leaf.moveToNextLeaf()) {
			System.out.println("can't move to the next leaf");
		}

		return false;
	}
	
	public boolean naiveGetNext() {
		if (!leaf.getNext()) {
			return false;
		}
		
		return true;
	}
	
	public TreeLeaf getTreeLeaf() {
		return leaf;
	}

	public boolean getNext() {
		if (!leaf.getNext()) {
			currentSearchkey = null;
			return false;
		}

		if (status == LT) {
			if (getDataVal().compareTo(currentSearchkey) < 0)
				return true;
		} else if (status == LTE) {
			if (getDataVal().compareTo(currentSearchkey) <= 0)
				return true;
		} else if (status == EQ) {
			if (getDataVal().compareTo(currentSearchkey) == 0)
				return true;
		} else if (status == GTE) {
			if (getDataVal().compareTo(currentSearchkey) >= 0)
				return true;
		} else if (status == GT) {
			if (getDataVal().compareTo(currentSearchkey) > 0)
				return true;
		}

		currentSearchkey = null;
		return false;
	}

	private Constant getDataVal() {
		return leaf.getDataVal();
	}

	public TupleToken getTupleToken() {
		return leaf.getTupleToken();
	}

	public void close() {
		if (leaf != null) {
			leaf.close();
		}
	}

	public void insert(Constant searchkey, TupleToken tt) {
		findBeforeFirst(searchkey);
		DirEntry de = leaf.insertTupleToken(tt);
		leaf.close();

		if (de == null) {
			return;
		}
        
		TreeDir newroot = new TreeDir(rootBlock, dirTkn, tx);
		DirEntry de2 = newroot.findInsertBlock(de);
		if (de2 != null) {
			newroot.createNewRoot(de2);
		}

		newroot.close();
	}

	public void delete(Constant searchkey, TupleToken tt) {
		findBeforeFirst(searchkey);
		leaf.delete(tt);
		leaf.close();
	}
    
	public void printLeafRecord() {
		findSmallestRecord();
		
		leaf.printRecord();
		
		while (leaf.moveToNextLeaf()) {
			System.out.println("????????????????????????????????????????????????");
			leaf.printRecord();
		}
	}

	public static int searchCost(int blocknum, int rpb) {
		return 1 + (int) (Math.log(blocknum) / Math.log(rpb));
	}
}